#include <fstream>
#include <QListWidget>
//#include <QStringList>

#include "contacts.h"

using namespace std;

Contacts::Contacts(QWidget *parent) : QMainWindow(parent), contactsWindow(new Ui::Contacts)
{
    contactsWindow->setupUi(this);

    // doesn't allow the contacts window to be resized
    setFixedSize(719, 296);

    // For an explanation of these variables, see the contacts.h file
    startUpIsOver_ = false;
    groupClicked_ = false;
    newContacts_ = true;
    noRealGroupChange_ = true;

    //used only for debugging
    contactsWindow->showContactsList->setHidden(true);
    connect(contactsWindow->showContactsList, SIGNAL(clicked()), this, SLOT(receivedDebug()));

    // send the contents to a bigger field to read in case the user can't see everything
    // from the cell
    connect(contactsWindow->contactsInfo, SIGNAL(itemClicked(QTableWidgetItem*)),
                                    this, SLOT(receiveContactClickedExpand()));
    connect(contactsWindow->contactsInfo, SIGNAL(currentItemChanged(QTableWidgetItem*,QTableWidgetItem*)),
                                    this, SLOT(receiveContactClickedExpand()));

    // makes sure we don't try to populate the contacts table when we have no contacts
    bool noContactsOnStartup = false;

    // checks if a group was clicked or selected with arrows
    connect(contactsWindow->contactsGroups, SIGNAL(itemClicked(QTableWidgetItem*)),
                                        this, SLOT(receiveGroupClicked()));
    connect(contactsWindow->contactsGroups, SIGNAL(currentItemChanged(QTableWidgetItem*,QTableWidgetItem*)),
                                        this, SLOT(receiveGroupSelectedWithArrows()));

    // checks to see if the user changes any information about a contact
    connect(contactsWindow->contactsInfo, SIGNAL(itemChanged(QTableWidgetItem*)),
                                        this, SLOT(receiveContactChanged()));

    // keeps track of the last group and contact cell clicked, respectively
    connect(contactsWindow->contactsGroups, SIGNAL(itemClicked(QTableWidgetItem*)),
                                        this, SLOT(receiveCellClicked()));
    connect(contactsWindow->contactsInfo, SIGNAL(itemClicked(QTableWidgetItem*)),
                                        this, SLOT(receiveContactClicked()));

    // when the user wants to delete a contact
    connect(contactsWindow->deleteContactButton, SIGNAL(clicked()),
                                        this, SLOT(receiveDeleteContactClicked()));

    // when the user wants to add a group
    connect(contactsWindow->addGroupsButton, SIGNAL(clicked()),
                                        this, SLOT(receiveAddGroupClicked()));

    // if the user changed the name of a group
    connect(contactsWindow->contactsGroups, SIGNAL(itemChanged(QTableWidgetItem*)),
                                        this, SLOT(receiveGroupChanged()));

    // when the save button is pushed
    connect(contactsWindow->saveButton, SIGNAL(clicked()),
                                    this, SLOT(receiveSaveButtonClicked()));

    // opens up contacts.txt, which holds all of the information for the user
    ifstream contactsTxt("contacts.txt", ios::in);

    // line is used to store each line that is being read from the file
    string line;

    // initialize the group count to 0
    groupCount_ = 0;

    // group table will always have 1 column
    // contacts table will always have 4 columns
    contactsWindow->contactsGroups->setColumnCount(1);
    contactsWindow->contactsInfo->setColumnCount(4);

    // sets the group table header
    QStringList ContactsGroups_TableHeader;
    ContactsGroups_TableHeader << tr("Groups");
    contactsWindow->contactsGroups->setHorizontalHeaderLabels(ContactsGroups_TableHeader);

    // populates headers of the contacts table
    ContactsInfo_TableHeaders << tr("Name") << tr("Email") << tr("Phone Number") << tr("Extra Info");
    populateContactsInfoHeaders();

    // checks to see if the contacts file exists
    if(!contactsTxt.is_open())
    {
        noContacts();
        noContactsOnStartup = true;
        fillBlankCells(5);
        contactsWindow->contactsGroups->item(0, 0)->setSelected(true);
        noRealGroupChange_ = false;
        startUpIsOver_ = true;
    }

    if(contactsTxt.is_open() && contactsTxt.good())
    {
        getline(contactsTxt, line, '\n');

        if(line.length() == 0)
        {
            getline(contactsTxt, line, '\n');

            // makes sure the contacts.txt file isn't empty even though it exists
            if(line.length() == 0)
            {
                noContacts();
                noContactsOnStartup = true;
                fillBlankCells(5);
                contactsWindow->contactsGroups->item(0, 0)->setSelected(true);
                noRealGroupChange_ = false;
                startUpIsOver_ = true;
            }
            // otherwise we can add what we found in the file to our contacts list
            else
            {
                contactsList.append(QString::fromStdString(line));
            }

        }
            // gets the rest of the items from the file
            while(!contactsTxt.eof())
            {
                getline(contactsTxt, line, '\n');
                if(line.length() != 0)
                {
                    contactsList.append(QString::fromStdString(line));
                }
            }
    }
    contactsTxt.close();

    // keeps track of if the current input from the the array is a group name or
    // a contact
    bool newGroup = true;
    bool newContacts = true;

    // makes sure there are contacts to begin with
    if(noContactsOnStartup == false)
    {
        // loops through all of the contacts and groups
        for(int i=0; i<contactsList.size(); i++)
        {

            // we know that a group will come after this "end group" statement
            if(contactsList.at(i) == "- End Group -")
            {
                newGroup = true;
                //contactsWindow->contactsGroup->addItem(contactsList.at(i));
            }
            // now we know this item is a new group name because it is the first
            // item after the "end group"
            if(newGroup == true && contactsList.at(i) != "- End Group -")
            {
                // add the group to the "groups" field
                contactsWindow->contactsGroups->insertRow(groupCount_);
                QTableWidgetItem *newItem = new QTableWidgetItem(contactsList.at(i));
                contactsWindow->contactsGroups->setItem(groupCount_, 0, newItem);
                groupCount_ += 1;
                newGroup = false;

            }
            // we check if there are contacts in the group
            if(newContacts == true && contactsList.at(i) != "- End Group -")
            {
                // this function will populate the contacts for each group
                populateContacts(i);
                newContacts = false;
            }


        }
        // selects the first group by default
        //contactsWindow->contactsGroups->selectedItems().append(contactsWindow->contactsGroups->item(0, 0));
        contactsWindow->contactsGroups->item(0, 0)->setSelected(true);
        startUpIsOver_ = true;
        newContacts_ = false;
        //addToConsole("Group Count: " + QString::number(groupCount_));

        if(groupCount_ < 5)
        {
            int neededGroups = 5 - groupCount_;
            fillBlankCells(neededGroups);
        }
        noRealGroupChange_ = false;
    } //end no contactsOnStartup
}

Contacts::~Contacts()
{
    delete contactsWindow;
}

// closes contacts window when exit button is clicked
void Contacts::on_exit_clicked()
{
    Contacts::close();
}

// puts messages into the main window output field
void Contacts::addToConsole(const QString & text)
{
    contactsWindow->output->append(text);
}

// Tells the user that he or she doesn't have any contacts
void Contacts::noContacts()
{
    addToConsole("You do not have any contacts. Please add a group and then some contacts.");
}

// displays the contacts in the group that was selected
void Contacts::receiveGroupClicked()
{
    groupClicked_ = true;
    contactsWindow->output->clear();

    // well the user just changed a group, so I think it's
    // safe to say they don't care about what is in the expand
    // field anymore.
    contactsWindow->expandField->clear();

    //addToConsole("Row Count: " + QString::number(contactsWindow->contactsInfo->rowCount()));

    if(contactsWindow->contactsInfo->rowCount() > 0)
    {
        for(int j = contactsWindow->contactsInfo->rowCount() - 1; j >= 0; j--)
        {
            contactsWindow->contactsInfo->removeRow(j);
            //addToConsole("Removed row at: " + QString::number(j));
        }
    }

    // it's .at(0) because we only care about the first item clicked
    QString actualGroupClicked = contactsWindow->contactsGroups->selectedItems().at(0)->text();

//addToConsole("Group Clicked: " + actualGroupClicked);

    if(contactsWindow->contactsGroups->selectedItems().isEmpty())
    {
        addToConsole("You have not selected a valid group.");
        return;
    }

    for(int i = 0; i < contactsList.size(); i++)
    {
        // cells with ((NO DATA)) should never have contacts in them
        if(contactsList.at(i) == actualGroupClicked && actualGroupClicked != "((NO DATA))")
        {
            populateContacts(i);
        }
    }
    groupClicked_ = false;
}

// populates the contacts from each group
void Contacts::populateContacts(int i)
{
    // keeps track of each contact in the group,
    // so then each contact has its own row
    int k = 0;

    // increment i so we are either at the first contact's name or
    // at the end - End Group -. If we are at the end group thing,
    // we know the group is empty
    i++;
    if(contactsList.at(i) == "- End Group -")
    {
        addToConsole("You do not have any contacts for the group: " + contactsList.at(--i));
    }

    else
    {
        // we know we have contacts, so now we load all of the contacts from
        // this group.
        while(contactsList.at(i) != "- End Group -")
        {
            // insert a new row
            contactsWindow->contactsInfo->insertRow(k);

            /*
                Each contact has 4 information to go with it:
                1. Name
                2. Email address
                3. Phone number
                4. Any extra information the user wants to provide
            */
            for(int j = 0; j < 4; j++)
            {
                QTableWidgetItem *newItem = new QTableWidgetItem(contactsList.at(i));
                contactsWindow->contactsInfo->setItem(k, j, newItem);
                i++;
            }
            // moves on to make a new row for the next contact
            k++;
        }
    }
}
// populates headers of contacts table
void Contacts::populateContactsInfoHeaders()
{
    contactsWindow->contactsInfo->setHorizontalHeaderLabels(ContactsInfo_TableHeaders);
}

void Contacts::on_deleteGroupButton_clicked()
{
    /*
        When we hit the delete button, the "receiveGroupChanged" function
        is called and run through on accident. Since that function is only
        supposed to be called when a user modifies an existing contact's info,
        we want to make sure it doesn't run through when we delete a group.
        This prevents that.
    */
    noRealGroupChange_ = true;

    /*
        correctIndex is used for deleting a group that still has the default
        value ((NO DATA)). This is needed because the user might decide
        to add a group after a group that still has ((NO DATA)) in it.

        currentlyFound keeps track of how many groups have been found
        up to that point.
    */

    int correctIndex = contactsWindow->contactsGroups->currentRow();
    int currentlyFound = 0;
    bool foundRightOne = false;

    if(cellClicked != "((NO DATA))")
    {
        // we only need to search for the "right one" if it is
        // equal to "((NO DATA))"
        foundRightOne = true;
    }

    for(int j = 0; j < contactsList.size(); j++)
    {
        // found the right one when we have searched for the
        // correct number of default cells with ((NO DATA))
        if(currentlyFound == correctIndex)
        {
            foundRightOne = true;
        }

        // we have reached the end of a group, so we know we will find
        // another after it, if there is one
        if(contactsList.at(j) == "- End Group -")
        {
            currentlyFound += 1;
        }

        // removes the entire group once it has been found
        if(QString::compare(contactsList.at(j), contactsWindow->contactsGroups->selectedItems().at(0)->text()) == 0
            && foundRightOne == true)
        {
            //addToConsole("contactsList.at(j): " + contactsList.at(j));
            while(contactsList.at(j) != "- End Group -")
            {
                addToConsole("Removing: " + contactsList.at(j));
                contactsList.removeAt(j);
            }
            // removes the "- End Group -" part too
            addToConsole("Removing: " + contactsList.at(j));
            contactsList.removeAt(j);
            break;
        }
    }

    // removes the current row and sets the focus back to the first item in the table
    contactsWindow->contactsGroups->removeRow(contactsWindow->contactsGroups->currentRow());
    contactsWindow->contactsGroups->setCurrentCell(0, 0);

    // makes sure we always have at least 5 groups showing in the groups table,
    // even if they are the default ones
    if(contactsWindow->contactsGroups->rowCount() < 5)
    {
        // way of figuring out how many rows are missing to make 5
        int neededRows = 5 - contactsWindow->contactsGroups->rowCount();
        // new row goes here because row indexes start at 0.
        // ex) with 3 rows, they have positions 0, 1, and 2 with a row count = 3
        // therefore, the new row will have to go in position 3
        int newRowPos = contactsWindow->contactsGroups->rowCount();

        // fill in all the needed rows
        for(int i = 0; i < neededRows; i++)
        {
            // add them to our list
            contactsList.append("((NO DATA))");
            contactsList.append("- End Group -");

            // add them to the GUI
            contactsWindow->contactsGroups->insertRow(newRowPos);
            QTableWidgetItem *newItem = new QTableWidgetItem("((NO DATA))");
            contactsWindow->contactsGroups->setItem(newRowPos, 0, newItem);
        }
        noRealGroupChange_ = false;
        addToConsole("You have successfully deleted a group.");
        return;
    }
}

void Contacts::receiveContactChanged()
{
    // make sure we don't run through this function on startup and
    // whenever a group wasn't clicked
    if (startUpIsOver_ == true && groupClicked_ == false)
    {
        //addToConsole("Group: " + contactsWindow->contactsGroups->selectedItems().at(0)->text());
        //addToConsole("Old Item: " + cellClicked);
        //addToConsole("New Contact Item: " + contactsWindow->contactsInfo->currentItem()->text());
        //addToConsole("This is in cell: " + QString::number(contactsWindow->contactsInfo->currentRow())
                     //+ ", " + QString::number(contactsWindow->contactsInfo->currentColumn()));

        // makes sure the user doesn't leave a cell blank, which could
        // cause the program to crash
        if(contactsWindow->contactsInfo->currentItem()->text().isEmpty())
        {
            addToConsole("You must insert a non-blank item");

            // set the item back to what it originally was
            contactsWindow->contactsInfo->currentItem()->setText(contactClicked_);
            return;
        }

        // otherwise, search for the group that was clicked
        for(int i = 0; i < contactsList.size(); i++)
        {
            // then once we find it...
            if(QString::compare(contactsList.at(i), contactsWindow->contactsGroups->selectedItems().at(0)->text()) == 0)
            {
                /*
                    Uses this formula to find out which exact cell has been changed
                    this actually allows the user to have some contact information be the same
                    in different cells. for example, I might have the same address as my parents
                    right now, so that information should be the same. Therefore, it is important to allow
                    some cells to have the same information especially if my parents and I
                    are listed as differnt contacts. This allows that, and it will change
                    everything correctly.
                */
                int k = ((4 * contactsWindow->contactsInfo->currentRow()) + 1) +
                        contactsWindow->contactsInfo->currentColumn();
                i += k;
                contactsList.replace(i, contactsWindow->contactsInfo->currentItem()->text());
                break;
            }
        }
    }
}

void Contacts::receiveCellClicked()
{
    // keeps track of the group that was last clicked
    cellClicked = contactsWindow->contactsGroups->selectedItems().at(0)->text();
}

void Contacts::receiveContactClicked()
{
    // keeps track of the contact that was last clicked
    contactClicked_ = contactsWindow->contactsInfo->selectedItems().at(0)->text();
}

// makes sure there are always 5 groups loaded in the GUI because
// otherwise the user could click on a blank space and cause the
// program to crash
void Contacts::fillBlankCells(int neededGroups)
{    
    int a = contactsWindow->contactsGroups->rowCount();
    for(int i = 0; i < neededGroups; i++)
    {
        //QTableWidgetItem *newItem = new QTableWidgetItem("((NO DATA))");
        contactsList.append("((NO DATA))");
        contactsList.append("- End Group -");
        contactsWindow->contactsGroups->insertRow(a);
        QTableWidgetItem *newItem = new QTableWidgetItem("((NO DATA))");
        contactsWindow->contactsGroups->setItem(a, 0, newItem);
        a++;
    }

}

void Contacts::on_addContactsButton_clicked()
{
    //addToConsole("add contacts was clicked");
    bool findGroup = false;

    // makes sure the user doesn't add a contact to the default group name
    // makes the user change the name of the group from that
    if(contactsWindow->contactsGroups->selectedItems().at(0)->text() == "((NO DATA))")
    {
        contactsWindow->output->clear();
        addToConsole("You must rename this group before you can add contacts to it.");
        addToConsole("To rename a group, double click the cell and type.");
        addToConsole("When you're finished, you may press enter.");
        return;
    }


    for(int i = 0; i < contactsList.size(); i++)
    {
        // search for the group we want to add the contacts to, which is the selected group
        if(QString::compare(contactsList.at(i), contactsWindow->contactsGroups->selectedItems().at(0)->text()) == 0)
        {
            findGroup = true;
        }

        // when the group is found and we are at the end of the contact list for THAT group,
        // then we add this new row to the very end. This way the new row will show up
        // when the group is clicked again because the table is populated from this list
        if(findGroup == true && contactsList.at(i) == "- End Group -")
        {
            int count = 0;
            //row = contactsWindow->contactsInfo->rowCount();
            //contactsWindow->contactsInfo->insertRow(row);
            for (int j = i; count < 4; count++)
            {
                contactsList.insert(j, "((NO DATA))");
                //QTableWidgetItem *newItem = new QTableWidgetItem("((NO DATA))");
                //contactsWindow->contactsInfo->setItem(row, count, newItem);
                //addToConsole("Row: " + QString::number(row) + "   count: " + QString::number(count));
                //addToConsole("contactsList.at(j): " + contactsList.at(j));
                j++;
            }
            //refresh the table
            receiveGroupClicked();
            contactsWindow->contactsInfo->scrollToBottom();
            addToConsole("You have added a new contact.");
            addToConsole("To edit, double click the cell and start typing.");
            addToConsole("When you're finished, you may press enter.");
            break;
        }
    }
}

void Contacts::receiveDeleteContactClicked()
{
    //addToConsole("In receiveDeleteContactClicked()");
    //addToConsole("Group: " + contactsWindow->contactsGroups->selectedItems().at(0)->text());
    //addToConsole("This is in cell: " + QString::number(contactsWindow->contactsInfo->currentRow())
    //+ ", " + QString::number(contactsWindow->contactsInfo->currentColumn()));

    // makes sure a contact is selected to be deleted
    if(contactsWindow->contactsInfo->currentRow() == -1 ||
       contactsWindow->contactsInfo->currentColumn() == -1)
    {
        addToConsole("You have not selected a contact to delete.");
        return;
    }

    // makes sure the user clicks the "name" part of the contact
    if(contactsWindow->contactsInfo->currentColumn() != 0)
    {
        addToConsole("You must first click the actual name of the contact you want to delete");
        return;
    }

    for(int i = 0; i < contactsList.size(); i++)
    {
        // search for the group we want to remove a contact from
        if(QString::compare(contactsList.at(i), contactsWindow->contactsGroups->selectedItems().at(0)->text()) == 0)
        {
            //addToConsole("Found the group, time to start deleting the contact!");

            //removes the correct contact the user wanted to delete
            int k = (4 * contactsWindow->contactsInfo->currentRow()) + 1;
            i += k;
            for(int j = 0; j < 4; j++)
            {
                contactsList.removeAt(i);
            }
            addToConsole("You have successfully deleted a contact.");

            // refreshes the table
            receiveGroupClicked();
            break;
        }
    }
}

void Contacts::receiveAddGroupClicked()
{
    // you aren't changing the name of an existing group, you're just adding a group
    // This will make it so "receiveGroupChanged" isn't also called
    noRealGroupChange_ = true;

    //addToConsole("You want to add a group!");

    //adds his default information for the new group to the array
    contactsList.append("((NO DATA))");
    contactsList.append("- End Group -");

    // updates the GUI
    contactsWindow->contactsGroups->insertRow(contactsWindow->contactsGroups->rowCount());
    QTableWidgetItem *newItem = new QTableWidgetItem("((NO DATA))");
    contactsWindow->contactsGroups->setItem(contactsWindow->contactsGroups->rowCount() - 1, 0, newItem);
    contactsWindow->contactsGroups->scrollToBottom();
    addToConsole("You have succesfully added a new group!");
    noRealGroupChange_ = false;
}

void Contacts::receiveGroupChanged()
{
    if(noRealGroupChange_ == false)
    {
        //addToConsole("A group was really changed");
        /*
          this will hold how many "- End Group -"s we will have to find
          this is important when changing a cell with ((NO DATA)) in it
          because these can be all throughout the QList, so we have to make
          sure we find the right one.
        */
        int correctIndex = contactsWindow->contactsGroups->currentRow();
        int currentlyFound = 0;
        bool foundRightOne = false;

        //addToConsole("changing a group");

        if(contactsWindow->contactsGroups->currentItem()->text().isEmpty())
        {
            addToConsole("You must insert a non-blank name");
            contactsWindow->contactsGroups->currentItem()->setText(cellClicked);
            return;
        }

        if(cellClicked != "((NO DATA))")
        {
            // we only need to search for the "right one" if it is
            // equal to "((NO DATA))"
            foundRightOne = true;
        }
//addToConsole("correct index: " + QString::number(correctIndex));
//addToConsole("current row: " + QString::number(contactsWindow->contactsGroups->currentRow()));
        for(int i = 0; i < contactsList.size(); i++)
        {
            //addToConsole("Current: " + contactsList.at(i));
            if(currentlyFound == correctIndex)
            {
                foundRightOne = true;
            }

            if(contactsList.at(i) == "- End Group -")
            {
                currentlyFound += 1;
            }

            // If we finally found the right one to change, then we can make the change.
            if(QString::compare(contactsList.at(i), cellClicked) == 0 && foundRightOne == true)
            {
                addToConsole("New group name: " + contactsWindow->contactsGroups->currentItem()->text());
                contactsList.replace(i, contactsWindow->contactsGroups->currentItem()->text());
                break;
            }
        }
    }
}

void Contacts::receiveSaveButtonClicked()
{
    ofstream contactsTxt("contacts.txt", ios::out | ios::trunc);

    // doesn't save if there is nothing to save
    if(contactsList.isEmpty())
    {
        addToConsole("The contacts list is empty and can't be saved");
        return;
    }

    // otherwise it writes the changes to the file
    if(contactsTxt.is_open() && contactsTxt.good() )
    {
        contactsTxt << endl;
        for(int i = 0; i < contactsList.size(); i++)
        {
            contactsTxt << contactsList.at(i).toStdString() << endl;
        }
        addToConsole("Done Saving!");
    }
}

// used for debugging. It displays the entire list.
void Contacts::receivedDebug()
{
    for(int i = 0; i < contactsList.size(); i++)
    {
        addToConsole("DEBUGGING: " + contactsList.at(i));
    }
}

void Contacts::receiveContactClickedExpand()
{
    // check to see if an item from the contacts table is selected
    if(contactsWindow->contactsInfo->selectedItems().isEmpty())
    {
        //addToConsole("Please select an item from the contacts table to see");
        //addToConsole("the information in the window to the right.");
        return;
    }

    // otherwise, we can append the selected text to the text field
    contactsWindow->expandField->clear();
    contactsWindow->expandField->append(contactsWindow->contactsInfo->selectedItems().at(0)->text());
}

// makes sure that the "clicked" signal is emitted
// when the user selects different groups with the arrow keys
void Contacts::receiveGroupSelectedWithArrows()
{
    receiveCellClicked();
    receiveGroupClicked();
}
